home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / misc / volume20 / jargon / part01 next >
Encoding:
Text File  |  1991-06-23  |  51.3 KB  |  1,682 lines

  1. Newsgroups: comp.sources.misc
  2. From: Richard Goerwitz <goer@sophist.uchicago.edu>
  3. Subject:  v20i063:  jargon - jargon browser, Part01/01
  4. Message-ID: <1991Jun20.033537.10417@sparky.IMD.Sterling.COM>
  5. X-Md4-Signature: 3ce443f32cac4cb8f5c261e28f8ef252
  6. Date: Thu, 20 Jun 1991 03:35:37 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: Richard Goerwitz <goer@sophist.uchicago.edu>
  10. Posting-number: Volume 20, Issue 63
  11. Archive-name: jargon/part01
  12. Environment: Icon
  13.  
  14. Program name:  jargon
  15. Source language:  icon
  16. Purpose:  quickly find entries in the hackers' jargon file
  17.  
  18. This shell archive contains a jargon database program, aptly enough
  19. named "jargon."  If you have version 2.7.1 (posted March 1, 1991 to
  20. alt. sources) or version 2.8.[1-3] (ftp from pit-manager.mit.edu,
  21. pub/jargon/jargon2.8.3.Z) of the hackers' jargon file, you can use
  22. this package for quick access to entries within that file.  Just type
  23. "jargon word" (where "word" is the bit of hackers' jargon you want a
  24. definition for).  If you're not sure what's in the database, type
  25. "jargon -p pattern" (where pattern is an egrep-style regular
  26. expression).  Jargon will print a list of all entries containing
  27. pattern to the standard output.  Uppercase letters are folded into
  28. their lowercase equivalents, incidentally, in order to provide enough
  29. latitude for all the different capitalization practices.
  30.  
  31. Richard Goerwitz (goer@sophist.uchicago.edu)
  32. ---
  33. #! /bin/sh
  34. # This is a shell archive.  Remove anything before this line, then feed it
  35. # into a shell via "sh file" or similar.  To overwrite existing files,
  36. # type "sh file -c".
  37. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  38. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  39. # Contents:  README Makefile.dist adjuncts.icn findre.icn getkeys.icn
  40. #   gettext.icn idxtext.icn jarg2get.icn jargon.src
  41. # Wrapped by kent@sparky on Wed Jun 19 22:17:50 1991
  42. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  43. echo If this archive is complete, you will see the following message:
  44. echo '          "shar: End of archive 1 (of 1)."'
  45. if test -f 'README' -a "${1}" != "-c" ; then 
  46.   echo shar: Will not clobber existing file \"'README'\"
  47. else
  48.   echo shar: Extracting \"'README'\" \(4444 characters\)
  49.   sed "s/^X//" >'README' <<'END_OF_FILE'
  50. X-------
  51. X
  52. XProgram name:  jargon
  53. XSource language:  icon
  54. XPurpose:  quickly find entries in the hackers' jargon file
  55. X
  56. X-------
  57. X
  58. XDescription:
  59. X
  60. XThis shell archive contains a jargon database program, aptly enough
  61. Xnamed "jargon."  If you have version 2.7.1 (posted March 1, 1991 to
  62. Xalt. sources) or version 2.8.[1-3] (ftp from pit-manager.mit.edu,
  63. Xpub/jargon/jargon2.8.3.Z) of the hackers' jargon file, you can use
  64. Xthis package for quick access to entries within that file.  Just type
  65. X"jargon word" (where "word" is the bit of hackers' jargon you want a
  66. Xdefinition for).  If you're not sure what's in the database, type
  67. X"jargon -p pattern" (where pattern is an egrep-style regular
  68. Xexpression).  Jargon will print a list of all entries containing
  69. Xpattern to the standard output.  Uppercase letters are folded into
  70. Xtheir lowercase equivalents, incidentally, in order to provide enough
  71. Xlatitude for all the different capitalization practices.
  72. X
  73. X-------
  74. X
  75. XInstallation:
  76. X
  77. XCp Makefile.dist to Makefile and edit it to reflect local file
  78. Xstructure and ownership conventions.  After editing, make all.  If,
  79. Xafter seeing a lot of garbage go by, you get the message "everything
  80. Xseems OK," then su root and make install.  If you don't have root
  81. Xprivileges, you must change the DESTDIR and LIBDIR variables in the
  82. Xmakefile so that they reflect directories you have access to.
  83. X
  84. XIf you have an Icon implementation that doesn't support expandable
  85. Xregions, then you may need to adjust QLSIZE and HEAPSIZE (on which,
  86. Xsee the icont man page).
  87. X
  88. XIf you are operating on a non-Unix platform, you'll need to edit the
  89. Xfile "jargon.src" so that the variable "database" is set to the full
  90. Xpath of the jargon.wrd file (i.e. $(LIBDIR)/jargon.wrd in the
  91. Xmakefile).  Then type:
  92. X
  93. X    icont jarg2get.icn
  94. X    icont -o idxtext idxtext.icn adjuncts.icn
  95. X    copy jargon.src jargon.icn
  96. X    icont -o jargon jargon.icn gettext.icn adjuncts.icn \
  97. X        getkeys.icn findre.icn
  98. X    (iconx) jarg2get < JARGONFILE > jargon.wrd
  99. X    (iconx) idxtext jargon.wrd
  100. X
  101. Xwhere "copy" is your system's file copy or rename command, and
  102. XJARGONFILE is the name of the original jargon.ascii file.  Note that
  103. Xthe backslash above merely indicates that the next line should be
  104. Xtyped together with the current line.  It is not to be entered
  105. Xliterally.  On some systems, it may be necessary to type "iconx"
  106. Xin as indicated above in parentheses (in some cases, "ficonx").
  107. X
  108. XTo test a non-UNIX installation, type
  109. X
  110. X    (iconx) jargon zork
  111. X
  112. XAgain, "iconx" may not be necessary.  If you get a definition for
  113. X"zork," then everything is probably OK.  If you altered jargon.src so
  114. Xthat the database variable points to a jargon.wrd file in the current
  115. Xdirectory, then there is no need to do anything more.  If you named a
  116. Xdatabase file in a different directory than the current one, then you
  117. Xshould copy the jargon.wrd file to that directory, cd to that direc-
  118. Xtory, then type
  119. X
  120. X    (iconx) idxtext DATABASE
  121. X
  122. Xwhere DATABASE gives the full path of your jargon.wrd file.  Running
  123. Xthis command may take a minute, so be patient.
  124. X
  125. XMS-DOS users:  Your small address space and segmented architecture
  126. Xmake it rough to install jargon.  If you are determined, then build
  127. Xthe files somewhere else.  Rename the .IDX file that is created dur-
  128. Xing this process as "argonwrd.idx."  Download this and the remaining
  129. Xfiles to your PC.  All you need to do on the PC is edit jargon.src
  130. Xto reflect where you want your jargon.wrd file to go, copy jargon.src
  131. Xto jargon.icn, then type 
  132. X
  133. X    icont -o jargon jargon.icn gettext.icn adjuncts.icn \
  134. X        getkeys.icn findre.icn
  135. X
  136. XI'd expect the same procedure to work for other micros, though I've
  137. Xonly tried it for PCs running MS-DOS.
  138. X
  139. X-------
  140. X
  141. XAdmission:
  142. X
  143. XJargon is really just a cheap trick I use to get people to test a
  144. Xsmall text retrieval package.  This package is fully documented in the
  145. Xfiles
  146. X
  147. X    gettext.icn
  148. X    getkeys.icn
  149. X    adjuncts.icn
  150. X    idxtext.icn
  151. X
  152. XIcon programmers will find gettext a nice, easy way to access
  153. Xkey/value combinations from a file (rather than from a hash table in
  154. Xmemory).  Often the practicalities of memory usage lead one to avoid
  155. Xhuge in-core hash tables.  The gettext package provides a small-scale,
  156. Xbut fairly efficient, alternative.
  157. X
  158. X-------
  159. X
  160. XProblems:
  161. X
  162. XThis program works fine on a Xenix/386 box.  Although I've heard many
  163. Xreports of it working elsewhere, your mileage may naturally vary.  If
  164. Xthere are problems, I'd like to hear about them.  Send mail to:
  165. X
  166. XRichard Goerwitz (goer@sophist.uchicago.edu)
  167. X
  168. END_OF_FILE
  169.   if test 4444 -ne `wc -c <'README'`; then
  170.     echo shar: \"'README'\" unpacked with wrong size!
  171.   fi
  172.   # end of 'README'
  173. fi
  174. if test -f 'Makefile.dist' -a "${1}" != "-c" ; then 
  175.   echo shar: Will not clobber existing file \"'Makefile.dist'\"
  176. else
  177.   echo shar: Extracting \"'Makefile.dist'\" \(2074 characters\)
  178.   sed "s/^X//" >'Makefile.dist' <<'END_OF_FILE'
  179. X# Don't change this unless you're absolutely sure of what you're doing.
  180. XPROGNAME = jargon
  181. X
  182. X# You may need to change these.
  183. XICONC = /usr/icon/v8/bin/icont
  184. XJARGONFILE = ./jargon2.8.3
  185. X
  186. X# Please edit these to reflect your local file structure & conventions.
  187. X# If you are running on a non-UNIX installation, see below at SRC1.
  188. XDESTDIR = /usr/local/bin
  189. XLIBDIR = /usr/local/lib/$(PROGNAME)
  190. XOWNER = root # bin
  191. XGROUP = root # bin
  192. X
  193. X# I hope you won't have to use this.
  194. XDEBUGFLAG = #-t
  195. X
  196. XSHELL = /bin/sh
  197. X# Source files for $(PROGNAME).  Uncomment NONUNIX for non-Unix
  198. X# installations.
  199. X# NONUNIX = getkeys.icn findre.icn
  200. XSRC1 = $(PROGNAME).icn gettext.icn adjuncts.icn $(NONUNIX)
  201. X
  202. Xall: jargon.wrd $(PROGNAME)
  203. X    @echo "\nEverything seems OK.  Go ahead & install.\n"
  204. X
  205. Xjargon.wrd: jarg2get idxtext
  206. X    test -f $(JARGONFILE)
  207. X    ./jarg2get < $(JARGONFILE) > jargon.wrd
  208. X    @echo "\nThis may take a few minutes:\n"
  209. X    ./idxtext jargon.wrd
  210. X
  211. X$(PROGNAME): $(SRC1)
  212. X    $(ICONC) $(DEBUGFLAG) -o $@ $(SRC1)
  213. X
  214. Xidxtext: idxtext.icn adjuncts.icn
  215. X    $(ICONC) $(DEBUGFLAG) -o $@ idxtext.icn adjuncts.icn
  216. X
  217. Xjarg2get: jarg2get.icn
  218. X    $(ICONC) $(DEBUGFLAG) -o $@ $?
  219. X
  220. X
  221. X# Set pathnames to their correct values for this system.
  222. X$(PROGNAME).icn:
  223. X    sed "s|/usr/local/lib/$(PROGNAME)/jargon.wrd|$(LIBDIR)/jargon.wrd|g" < jargon.src > jargon.icn
  224. X
  225. X# Pessimistic assumptions regarding the environment (in particular,
  226. X# I don't assume you have the BSD "install" shell script).
  227. Xinstall: all
  228. X    test -d $(DESTDIR) || (mkdir $(DESTDIR) && chmod 755 $(DESTDIR))
  229. X    cp $(PROGNAME) $(DESTDIR)/
  230. X    chgrp $(GROUP) $(DESTDIR)/$(PROGNAME)
  231. X    chown $(OWNER) $(DESTDIR)/$(PROGNAME)
  232. X    test -d $(LIBDIR) || (mkdir $(LIBDIR) && chmod 755 $(LIBDIR))
  233. X    cp jargon.wrd $(LIBDIR)/
  234. X    chgrp $(GROUP) $(LIBDIR)/jargon.wrd
  235. X    chown $(OWNER) $(LIBDIR)/jargon.wrd
  236. X    @echo "\nThis may take a few minutes:\n"
  237. X    ./idxtext $(LIBDIR)/jargon.wrd
  238. X    chgrp $(GROUP) $(LIBDIR)/*IDX
  239. X    chown $(OWNER) $(LIBDIR)/*IDX
  240. X    ./$(PROGNAME) zork
  241. X    @echo "\nEverything checks out OK.  Installation done.\n"
  242. X
  243. Xclean:
  244. X    -rm -f core *~ .u? *.IDX $(PROGNAME)
  245. X
  246. Xclobber: clean
  247. X    -rm -f $(PROGNAME).icn jargon.wrd
  248. X
  249. END_OF_FILE
  250.   if test 2074 -ne `wc -c <'Makefile.dist'`; then
  251.     echo shar: \"'Makefile.dist'\" unpacked with wrong size!
  252.   fi
  253.   # end of 'Makefile.dist'
  254. fi
  255. if test -f 'adjuncts.icn' -a "${1}" != "-c" ; then 
  256.   echo shar: Will not clobber existing file \"'adjuncts.icn'\"
  257. else
  258.   echo shar: Extracting \"'adjuncts.icn'\" \(1335 characters\)
  259.   sed "s/^X//" >'adjuncts.icn' <<'END_OF_FILE'
  260. X############################################################################
  261. X#
  262. X#    Name:     adjuncts.icn
  263. X#
  264. X#    Title:     adjuncts (adjunct utilities for gettext and idxtext)
  265. X#
  266. X#    Author:     Richard L. Goerwitz
  267. X#
  268. X#    Version: 1.2
  269. X#
  270. X############################################################################
  271. X#  
  272. X#  Pretty mundane stuff.  Basename(), Pathname(), Strip(), and a utility
  273. X#  for creating index filenames.
  274. X#
  275. X############################################################################
  276. X#
  277. X#  Links: none
  278. X#
  279. X#  See also: gettext.icn, idxtext,icn
  280. X#
  281. X############################################################################
  282. X
  283. X
  284. Xglobal _slash, _baselen
  285. X
  286. Xprocedure Basename(s)
  287. X
  288. X    # global _slash
  289. X    s ? {
  290. X    while tab(find(_slash)+1)
  291. X    return tab(0)
  292. X    }
  293. X
  294. Xend
  295. X
  296. X
  297. Xprocedure Pathname(s)
  298. X
  299. X    # global _slash
  300. X    s2 := ""
  301. X    s ? {
  302. X    while s2 ||:= tab(find(_slash)+1)
  303. X    return s2
  304. X    }
  305. X
  306. Xend
  307. X
  308. X
  309. Xprocedure getidxname(FNAME)
  310. X
  311. X    #
  312. X    # Discard path component.  Cut basename down to a small enough
  313. X    # size that the OS will be able to handle addition of the ex-
  314. X    # tension ".IDX"
  315. X    #
  316. X
  317. X    # global _slash, _baselen
  318. X    return right(Strip(Basename(FNAME,_slash),'.'), _baselen, "x") || ".IDX"
  319. X
  320. Xend
  321. X
  322. X
  323. Xprocedure Strip(s,c)
  324. X
  325. X    local s2
  326. X
  327. X    s2 := ""
  328. X    s ? {
  329. X    while s2 ||:= tab(upto(c))
  330. X    do tab(many(c))
  331. X    s2 ||:= tab(0)
  332. X    }
  333. X    return s2
  334. X
  335. Xend
  336. END_OF_FILE
  337.   if test 1335 -ne `wc -c <'adjuncts.icn'`; then
  338.     echo shar: \"'adjuncts.icn'\" unpacked with wrong size!
  339.   fi
  340.   # end of 'adjuncts.icn'
  341. fi
  342. if test -f 'findre.icn' -a "${1}" != "-c" ; then 
  343.   echo shar: Will not clobber existing file \"'findre.icn'\"
  344. else
  345.   echo shar: Extracting \"'findre.icn'\" \(20702 characters\)
  346.   sed "s/^X//" >'findre.icn' <<'END_OF_FILE'
  347. X########################################################################
  348. X#    
  349. X#    Name:    findre.icn
  350. X#    
  351. X#    Title:    "Find" Regular Expression
  352. X#    
  353. X#    Author:    Richard L. Goerwitz
  354. X#
  355. X#    Version: 1.14
  356. X#
  357. X########################################################################
  358. X#
  359. X#  I place this and any later versions in the public domain - RLG.
  360. X#
  361. X########################################################################
  362. X#
  363. X#  DESCRIPTION:  findre() is like the Icon builtin function find(),
  364. X#  except that it takes, as its first argument, a regular expression
  365. X#  pretty much like the ones the Unix egrep command uses (the few
  366. X#  minor differences are listed below).  Its syntax is the same as
  367. X#  find's (i.e. findre(s1,s2,i,j)), with the exception that a no-
  368. X#  argument invocation wipes out all static structures utilized by
  369. X#  findre, and then forces a garbage collection.
  370. X#
  371. X#  (For those not familiar with regular expressions and the Unix egrep
  372. X#  command: findre() offers a simple and compact wildcard-based search
  373. X#  system.  If you do a lot of searches through text files, or write
  374. X#  programs which do searches based on user input, then findre is a
  375. X#  utility you might want to look over.)
  376. X#
  377. X#  IMPORTANT DIFFERENCES between find and findre:  As noted above,
  378. X#  findre() is just a find() function that takes a regular expression
  379. X#  as its first argument.  One major problem with this setup is that
  380. X#  it leaves the user with no easy way to tab past a matched
  381. X#  substring, as with
  382. X# 
  383. X#    s ? write(tab(find("hello")+5))
  384. X#
  385. X#  In order to remedy this intrinsic deficiency, findre() sets the
  386. X#  global variable __endpoint to the first position after any given
  387. X#  match occurs.  Use this variable with great care, preferably
  388. X#  assigning its value to some other variable immediately after the
  389. X#  match (for example, findre("hello [.?!]*",s) & tmp := __endpoint).
  390. X#  Otherwise, you will certainly run into trouble.  (See the example
  391. X#  below for an illustration of how __endpoint is used).
  392. X#
  393. X#  IMPORTANT DIFFERENCES between egrep and findre:  findre utilizes
  394. X#  the same basic language as egrep.  The only big difference is that
  395. X#  findre uses intrinsic Icon data structures and escaping conven-
  396. X#  tions rather than those of any particular Unix variant.  Be care-
  397. X#  ful!  If you put findre("\(hello\)",s) into your source file,
  398. X#  findre will treat it just like findre("(hello)",s).  If, however,
  399. X#  you enter '\(hello\)' at run-time (via, say, findre(!&input,s)),
  400. X#  what Icon receives will depend on your operating system (most
  401. X#  likely, a trace will show "\\(hello\\)").
  402. X#
  403. X#  BUGS:  Space has essentially been conserved at the expense of time
  404. X#  in the automata produced by findre().  The algorithm, in other
  405. X#  words, will produce the equivalent of a pushdown automaton under
  406. X#  certain circumstances, rather than strive (at the expense of space)
  407. X#  for full determinism.  I tried to make up a nfa -> dfa converter
  408. X#  that would only create that portion of the dfa it needed to accept
  409. X#  or reject a string, but the resulting automaton was actually quite
  410. X#  slow (if anyone can think of a way to do this in Icon, and keep it
  411. X#  small and fast, please let us all know about it).  Note that under
  412. X#  version 8 of Icon, findre takes up negligible storage space, due to
  413. X#  the much improved hashing algorithm.  I have not tested it under
  414. X#  version 7, but I would expect it to use up quite a bit more space
  415. X#  in that environment.
  416. X#
  417. X#  IMPORTANT NOTE:  Findre takes a shortest-possible-match approach
  418. X#  to regular expressions.  In other words, if you look for "a*",
  419. X#  findre will not even bother looking for an "a."  It will just match
  420. X#  the empty string.  Without this feature, findre would perform a bit
  421. X#  more slowly.  The problem with such an approach is that often the
  422. X#  user will want to tab past the longest possible string of matched
  423. X#  characters (say tab((findre("a*|b*"), __endpoint)).  In circumstan-
  424. X#  ces like this, please just use something like:
  425. X#
  426. X#      s ? {
  427. X#          tab(find("a")) &  # or use Arb() from the IPL (patterns.icn)
  428. X#          tab(many('a'))
  429. X#          tab(many('b'))
  430. X#      }
  431. X#
  432. X#  or else use some combination of findre and the above.
  433. X#    
  434. X########################################################################
  435. X#
  436. X#  REGULAR EXPRESSION SYNTAX: Regular expression syntax is complex,
  437. X#  and yet simple.  It is simple in the sense that most of its power
  438. X#  is concentrated in about a dozen easy-to-learn symbols.  It is
  439. X#  complex in the sense that, by combining these symbols with
  440. X#  characters, you can represent very intricate patterns.
  441. X#
  442. X#  I make no pretense here of offering a full explanation of regular
  443. X#  expressions, their usage, and the deeper nuances of their syntax.
  444. X#  As noted above, this should be gleaned from a Unix manual.  For
  445. X#  quick reference, however, I have included a brief summary of all
  446. X#  the special symbols used, accompanied by an explanation of what
  447. X#  they mean, and, in some cases, of how they are used (most of this
  448. X#  is taken from the comments prepended to Jerry Nowlin's Icon-grep
  449. X#  command, as posted a couple of years ago):
  450. X#
  451. X#     ^   -  matches if the following pattern is at the beginning
  452. X#            of a line (i.e. ^# matches lines beginning with "#")
  453. X#     $   -  matches if the preceding pattern is at the end of a line
  454. X#     .   -  matches any single character
  455. X#     +   -  matches from 1 to any number of occurrences of the
  456. X#            previous expression (i.e. a character, or set of paren-
  457. X#            thesized/bracketed characters)
  458. X#     *   -  matches from 0 to any number of occurrences of the previous
  459. X#            expression
  460. X#     \   -  removes the special meaning of any special characters
  461. X#            recognized by this program (i.e if you want to match lines
  462. X#            beginning with a "[", write ^\[, and not ^[)
  463. X#     |   -  matches either the pattern before it, or the one after
  464. X#            it (i.e. abc|cde matches either abc or cde)
  465. X#     []  -  matches any member of the enclosed character set, or,
  466. X#            if ^ is the first character, any nonmember of the
  467. X#            enclosed character set (i.e. [^ab] matches any character
  468. X#         _except_ a and b).
  469. X#     ()  -  used for grouping (e.g. ^(abc|cde)$ matches lines consist-
  470. X#            ing of either "abc" or "cde," while ^abc|cde$ matches
  471. X#            lines either beginning with "abc" or ending in "cde")
  472. X#
  473. X#########################################################################
  474. X#
  475. X#  EXAMPLE program:
  476. X#
  477. X#  procedure main(a)
  478. X#      while line := !&input do {
  479. X#          token_list := tokenize_line(line,a[1])
  480. X#          every write(!token_list)
  481. X#      }
  482. X#  end
  483. X#
  484. X#  procedure tokenize_line(s,sep)
  485. X#      tmp_lst := []
  486. X#      s ? {
  487. X#          while field := tab(findre(sep)|0) &
  488. X#          mark := __endpoint
  489. X#          do {
  490. X#              put(tmp_lst,"" ~== field)
  491. X#              if pos(0) then break
  492. X#              else tab(mark)
  493. X#          }
  494. X#      }
  495. X#      return tmp_lst
  496. X#  end
  497. X#
  498. X#  The above program would be compiled with findre (e.g. "icont
  499. X#  test_prg.icn findre.icn") to produce a single executable which
  500. X#  tokenizes each line of input based on a user-specified delimiter.
  501. X#  Note how __endpoint is set soon after findre() succeeds.  Note
  502. X#  also how empty fields are excluded with "" ~==, etc.  Finally, note
  503. X#  that the temporary list, tmp_lst, is not needed.  It is included
  504. X#  here merely to illustrate one way in which tokens might be stored.
  505. X#
  506. X#  Tokenizing is, of course, only one of many uses one might put
  507. X#  findre to.  It is very helpful in allowing the user to construct
  508. X#  automata at run-time.  If, say, you want to write a program that
  509. X#  searches text files for patterns given by the user, findre would be
  510. X#  a perfect utility to use.  Findre in general permits more compact
  511. X#  expression of patterns than one can obtain using intrinsic Icon
  512. X#  scanning facilities.  Its near complete compatibility with the Unix
  513. X#  regexp library, moreover, makes for greater ease of porting,
  514. X#  especially in cases where Icon is being used to prototype C code.
  515. X#
  516. X#########################################################################
  517. X
  518. X
  519. Xglobal state_table, parends_present, slash_present
  520. Xglobal biggest_nonmeta_str, __endpoint
  521. Xrecord o_a_s(op,arg,state)
  522. X
  523. X
  524. Xprocedure findre(re, s, i, j)
  525. X
  526. X    local p, x, nonmeta_len
  527. X    static FSTN_table, STRING_table
  528. X    initial {
  529. X    FSTN_table := table()
  530. X    STRING_table := table()
  531. X    }
  532. X
  533. X    if /re then {
  534. X    FSTN_table := table()
  535. X    STRING_table := table()
  536. X    collect()  # do it *now*
  537. X    return
  538. X    }
  539. X
  540. X    /s := &subject
  541. X    if \i then {
  542. X    if i < 1 then
  543. X        i := *s + (i+1)
  544. X    }
  545. X    else i := \&pos | 1
  546. X    if \j then {
  547. X    if j < 1 then
  548. X        j := *s + (j+1)
  549. X    }
  550. X
  551. X    else j := *s+1
  552. X    if /FSTN_table[re] then {
  553. X    # If we haven't seen this re before, then...
  554. X    if \STRING_table[re] then {
  555. X        # ...if it's in the STRING_table, use plain find()
  556. X        every p := find(STRING_table[re],s,i,j)
  557. X        do { __endpoint := p + *STRING_table[re]; suspend p }
  558. X        fail
  559. X    }
  560. X    else {
  561. X        # However, if it's not in the string table, we have to
  562. X        # tokenize it and check for metacharacters.  If it has
  563. X        # metas, we create an FSTN, and put that into FSTN_table;
  564. X        # otherwise, we just put it into the STRING_table.
  565. X        tokenized_re := tokenize(re)
  566. X        if 0 > !tokenized_re then {
  567. X        # if at least one element is < 0, re has metas
  568. X        MakeFSTN(tokenized_re) | err_out(re,2)
  569. X        # both biggest_nonmeta_str and state_table are global
  570. X        /FSTN_table[re] := [.biggest_nonmeta_str, copy(state_table)]
  571. X        }
  572. X        else {
  573. X        # re has no metas; put the input string into STRING_table
  574. X        # for future reference, and execute find() at once
  575. X        tmp := ""; every tmp ||:= char(!tokenized_re)
  576. X        insert(STRING_table,re,tmp)
  577. X        every p := find(STRING_table[re],s,i,j)
  578. X        do { __endpoint := p + *STRING_table[re]; suspend p }
  579. X        fail
  580. X        }
  581. X    }
  582. X    }
  583. X
  584. X
  585. X    if nonmeta_len := (1 < *FSTN_table[re][1]) then {
  586. X    # If the biggest non-meta string in the original re
  587. X    # was more than 1, then put in a check for it...
  588. X    s[1:j] ? {
  589. X        tab(x := i to j - nonmeta_len) &
  590. X        (find(FSTN_table[re][1]) | fail) \ 1 &
  591. X        (__endpoint := apply_FSTN(&null,FSTN_table[re][2])) &
  592. X        (suspend x)
  593. X    }
  594. X    }
  595. X    else {
  596. X    #...otherwise it's not worth worrying about the biggest nonmeta str
  597. X    s[1:j] ? {
  598. X        tab(x := i to j) &
  599. X        (__endpoint := apply_FSTN(&null,FSTN_table[re][2])) &
  600. X        (suspend x)
  601. X    }
  602. X    }
  603. X
  604. Xend
  605. X
  606. X
  607. X
  608. Xprocedure apply_FSTN(ini,tbl)
  609. X
  610. X    static s_tbl
  611. X    local POS, tmp, fin
  612. X
  613. X    /ini := 1 & s_tbl := tbl & biggest_pos := 1
  614. X    if ini = 0 then {
  615. X    return &pos
  616. X    }
  617. X    POS := &pos
  618. X    fin := 0
  619. X
  620. X    repeat {
  621. X    if tmp := !s_tbl[ini] &
  622. X        tab(tmp.op(tmp.arg))
  623. X    then {
  624. X        if tmp.state = fin
  625. X        then return &pos
  626. X        else ini := tmp.state
  627. X    }
  628. X    else (&pos := POS, fail)
  629. X    }
  630. X
  631. Xend
  632. X    
  633. X
  634. X
  635. Xprocedure tokenize(s)
  636. X
  637. X    local chr, tmp
  638. X
  639. X    token_list := list()
  640. X    s ? {
  641. X    tab(many('*+?|'))
  642. X    while chr := move(1) do {
  643. X        if chr == "\\"
  644. X        # it can't be a metacharacter; remove the \ and "put"
  645. X        # the integer value of the next chr into token_list
  646. X        then put(token_list,ord(move(1))) | err_out(s,2,chr)
  647. X        else if any('*+()|?.$^',chr)
  648. X        then {
  649. X        # Yuck!  Egrep compatibility stuff.
  650. X        case chr of {
  651. X            "*"    : {
  652. X            tab(many('*+?'))
  653. X            put(token_list,-ord("*"))
  654. X            }
  655. X            "+"    : {
  656. X            tmp := tab(many('*?+')) | &null
  657. X            if upto('*?',\tmp)
  658. X            then put(token_list,-ord("*"))
  659. X            else put(token_list,-ord("+"))
  660. X            }
  661. X            "?"    : {
  662. X            tmp := tab(many('*?+')) | &null
  663. X            if upto('*+',\tmp)
  664. X            then put(token_list,-ord("*"))
  665. X            else put(token_list,-ord("?"))
  666. X            }
  667. X            "("    : {
  668. X            tab(many('*+?'))
  669. X            put(token_list,-ord("("))
  670. X            }
  671. X            default: {
  672. X            put(token_list,-ord(chr))
  673. X            }
  674. X        }
  675. X        }
  676. X        else {
  677. X        case chr of {
  678. X            # More egrep compatibility stuff.
  679. X            "["    : {
  680. X            b_loc := find("[") | *&subject+1
  681. X            every next_one := find("]",,,b_loc)
  682. X            \next_one ~= &pos | err_out(s,2,chr)
  683. X            put(token_list,-ord(chr))
  684. X            }
  685. X                    "]"    : {
  686. X            if &pos = (\next_one+1)
  687. X            then put(token_list,-ord(chr)) &
  688. X                 next_one := &null
  689. X            else put(token_list,ord(chr))
  690. X            }
  691. X            default: put(token_list,ord(chr))
  692. X        }
  693. X        }
  694. X    }
  695. X    }
  696. X
  697. X    token_list := UnMetaBrackets(token_list)
  698. X
  699. X    fixed_length_token_list := list(*token_list)
  700. X    every i := 1 to *token_list
  701. X    do fixed_length_token_list[i] := token_list[i]
  702. X    return fixed_length_token_list
  703. X
  704. Xend
  705. X
  706. X
  707. X
  708. Xprocedure UnMetaBrackets(l)
  709. X
  710. X    # Since brackets delineate a cset, it doesn't make
  711. X    # any sense to have metacharacters inside of them.
  712. X    # UnMetaBrackets makes sure there are no metacharac-
  713. X    # ters inside of the braces.
  714. X
  715. X    local tmplst, i, Lb, Rb
  716. X
  717. X    tmplst := list(); i := 0
  718. X    Lb := -ord("[")
  719. X    Rb := -ord("]")
  720. X
  721. X    while (i +:= 1) <= *l do {
  722. X    if l[i] = Lb then {
  723. X        put(tmplst,l[i])
  724. X        until l[i +:= 1] = Rb
  725. X        do put(tmplst,abs(l[i]))
  726. X        put(tmplst,l[i])
  727. X    }
  728. X    else put(tmplst,l[i])
  729. X    }
  730. X    return tmplst
  731. X
  732. Xend
  733. X
  734. X
  735. X
  736. Xprocedure MakeFSTN(l,INI,FIN)
  737. X
  738. X    # MakeFSTN recursively descends through the tree structure
  739. X    # implied by the tokenized string, l, recording in (global)
  740. X    # fstn_table a list of operations to be performed, and the
  741. X    # initial and final states which apply to them.
  742. X
  743. X    # global biggest_nonmeta_str, slash_present, parends_present
  744. X    static Lp, Rp, Sl, Lb, Rb, Caret_inside, Dot, Dollar, Caret_outside
  745. X    local i, inter, inter2, tmp
  746. X    initial {
  747. X    Lp := -ord("("); Rp := -ord(")")
  748. X    Sl := -ord("|")
  749. X    Lb := -ord("["); Rb := -ord("]"); Caret_inside := ord("^")
  750. X    Dot := -ord("."); Dollar := -ord("$"); Caret_outside := -ord("^")
  751. X    }
  752. X
  753. X    /INI := 1 & state_table := table() &
  754. X    NextState("new") & biggest_nonmeta_str := ""
  755. X    /FIN := 0
  756. X
  757. X    # I haven't bothered to test for empty lists everywhere.
  758. X    if *l = 0 then {
  759. X    /state_table[INI] := []
  760. X    put(state_table[INI],o_a_s(zSucceed,&null,FIN))
  761. X    return
  762. X    }
  763. X
  764. X    # HUNT DOWN THE SLASH (ALTERNATION OPERATOR)
  765. X    every i := 1 to *l do {
  766. X    if l[i] = Sl & tab_bal(l,Lp,Rp) = i then {
  767. X        if i = 1 then err_out(l,2,char(abs(l[i]))) else {
  768. X        /slash_present := "yes"
  769. X        inter := NextState()
  770. X        inter2:= NextState()
  771. X        MakeFSTN(l[1:i],inter2,FIN)
  772. X        MakeFSTN(l[i+1:0],inter,FIN)
  773. X        /state_table[INI] := []
  774. X        put(state_table[INI],o_a_s(apply_FSTN,inter2,0))
  775. X        put(state_table[INI],o_a_s(apply_FSTN,inter,0))
  776. X        return
  777. X        }
  778. X    }
  779. X    }
  780. X
  781. X    # HUNT DOWN PARENTHESES
  782. X    if l[1] = Lp then {
  783. X    i := tab_bal(l,Lp,Rp) | err_out(l,2,"(")
  784. X    inter := NextState()
  785. X    if any('*+?',char(abs(0 > l[i+1]))) then {
  786. X        case l[i+1] of {
  787. X        -ord("*")   : {
  788. X            /state_table[INI] := []
  789. X            put(state_table[INI],o_a_s(apply_FSTN,inter,0))
  790. X            MakeFSTN(l[2:i],INI,INI)
  791. X            MakeFSTN(l[i+2:0],inter,FIN)
  792. X            return
  793. X        }
  794. X        -ord("+")   : {
  795. X            inter2 := NextState()
  796. X            /state_table[inter2] := []
  797. X            MakeFSTN(l[2:i],INI,inter2)
  798. X            put(state_table[inter2],o_a_s(apply_FSTN,inter,0))
  799. X            MakeFSTN(l[2:i],inter2,inter2)
  800. X            MakeFSTN(l[i+2:0],inter,FIN)
  801. X            return
  802. X        }
  803. X        -ord("?")   : {
  804. X            /state_table[INI] := []
  805. X            put(state_table[INI],o_a_s(apply_FSTN,inter,0))
  806. X            MakeFSTN(l[2:i],INI,inter)
  807. X            MakeFSTN(l[i+2:0],inter,FIN)
  808. X            return
  809. X        }
  810. X        }
  811. X    }
  812. X    else {
  813. X        MakeFSTN(l[2:i],INI,inter)
  814. X        MakeFSTN(l[i+1:0],inter,FIN)
  815. X        return
  816. X    }
  817. X    }
  818. X    else {     # I.E. l[1] NOT = Lp (left parenthesis as -ord("("))
  819. X    every i := 1 to *l do {
  820. X        case l[i] of {
  821. X        Lp     : {
  822. X            inter := NextState()
  823. X            MakeFSTN(l[1:i],INI,inter)
  824. X            /parends_present := "yes"
  825. X            MakeFSTN(l[i:0],inter,FIN)
  826. X            return
  827. X        }
  828. X        Rp     : err_out(l,2,")")
  829. X        }
  830. X    }
  831. X    }
  832. X
  833. X    # NOW, HUNT DOWN BRACKETS
  834. X    if l[1] = Lb then {
  835. X    i := tab_bal(l,Lb,Rb) | err_out(l,2,"[")
  836. X    inter := NextState()
  837. X    tmp := ""; every tmp ||:= char(l[2 to i-1])
  838. X    if Caret_inside = l[2]
  839. X    then tmp := ~cset(Expand(tmp[2:0]))
  840. X    else tmp :=  cset(Expand(tmp))
  841. X    if any('*+?',char(abs(0 > l[i+1]))) then {
  842. X        case l[i+1] of {
  843. X        -ord("*")   : {
  844. X            /state_table[INI] := []
  845. X            put(state_table[INI],o_a_s(apply_FSTN,inter,0))
  846. X            put(state_table[INI],o_a_s(any,tmp,INI))
  847. X            MakeFSTN(l[i+2:0],inter,FIN)
  848. X            return
  849. X        }
  850. X        -ord("+")   : {
  851. X            inter2 := NextState()
  852. X            /state_table[INI] := []
  853. X            put(state_table[INI],o_a_s(any,tmp,inter2))
  854. X            /state_table[inter2] := []
  855. X            put(state_table[inter2],o_a_s(apply_FSTN,inter,0))
  856. X            put(state_table[inter2],o_a_s(any,tmp,inter2))
  857. X            MakeFSTN(l[i+2:0],inter,FIN)
  858. X            return
  859. X        }
  860. X        -ord("?")   : {
  861. X            /state_table[INI] := []
  862. X            put(state_table[INI],o_a_s(apply_FSTN,inter,0))
  863. X            put(state_table[INI],o_a_s(any,tmp,inter))
  864. X            MakeFSTN(l[i+2:0],inter,FIN)
  865. X            return
  866. X        }
  867. X        }
  868. X    }
  869. X    else {
  870. X        /state_table[INI] := []
  871. X        put(state_table[INI],o_a_s(any,tmp,inter))
  872. X        MakeFSTN(l[i+1:0],inter,FIN)
  873. X        return
  874. X    }
  875. X    }
  876. X    else {           # I.E. l[1] not = Lb
  877. X    every i := 1 to *l do {
  878. X        case l[i] of {
  879. X        Lb     : {
  880. X            inter := NextState()
  881. X            MakeFSTN(l[1:i],INI,inter)
  882. X            MakeFSTN(l[i:0],inter,FIN)
  883. X            return
  884. X        }
  885. X        Rb     : err_out(l,2,"]")
  886. X        }
  887. X    }
  888. X    }
  889. X
  890. X    # FIND INITIAL SEQUENCES OF POSITIVE INTEGERS, CONCATENATE THEM
  891. X    if i := match_positive_ints(l) then {
  892. X    inter := NextState()
  893. X    tmp := Ints2String(l[1:i])
  894. X    # if a slash has been encountered already, forget optimizing
  895. X        # in this way; if parends are present, too, then forget it,
  896. X        # unless we are at the beginning or end of the input string
  897. X    if  INI = 1 | FIN = 2 | /parends_present &
  898. X        /slash_present & *tmp > *biggest_nonmeta_str
  899. X    then biggest_nonmeta_str := tmp
  900. X    /state_table[INI] := []
  901. X    put(state_table[INI],o_a_s(match,tmp,inter))
  902. X    MakeFSTN(l[i:0],inter,FIN)
  903. X    return
  904. X    }
  905. X
  906. X    # OKAY, CLEAN UP ALL THE JUNK THAT'S LEFT
  907. X    i := 0
  908. X    while (i +:= 1) <= *l do {
  909. X    case l[i] of {
  910. X        Dot          : { Op := any;   Arg := &cset }
  911. X        Dollar       : { Op := pos;   Arg := 0     }
  912. X        Caret_outside: { Op := pos;   Arg := 1     }
  913. X        default      : { Op := match; Arg := char(0 < l[i]) }
  914. X    } | err_out(l,2,char(abs(l[i])))
  915. X    inter := NextState()
  916. X    if any('*+?',char(abs(0 > l[i+1]))) then {
  917. X        case l[i+1] of {
  918. X        -ord("*")   : {
  919. X            /state_table[INI] := []
  920. X            put(state_table[INI],o_a_s(apply_FSTN,inter,0))
  921. X            put(state_table[INI],o_a_s(Op,Arg,INI))
  922. X            MakeFSTN(l[i+2:0],inter,FIN)
  923. X            return
  924. X        }
  925. X        -ord("+")   : {
  926. X            inter2 := NextState()
  927. X            /state_table[INI] := []
  928. X            put(state_table[INI],o_a_s(Op,Arg,inter2))
  929. X            /state_table[inter2] := []
  930. X            put(state_table[inter2],o_a_s(apply_FSTN,inter,0))
  931. X            put(state_table[inter2],o_a_s(Op,Arg,inter2))
  932. X            MakeFSTN(l[i+2:0],inter,FIN)
  933. X            return
  934. X        }
  935. X        -ord("?")   : {
  936. X            /state_table[INI] := []
  937. X            put(state_table[INI],o_a_s(apply_FSTN,inter,0))
  938. X            put(state_table[INI],o_a_s(Op,Arg,inter))
  939. X            MakeFSTN(l[i+2:0],inter,FIN)
  940. X            return
  941. X        }
  942. X        }
  943. X    }
  944. X    else {
  945. X        /state_table[INI] := []
  946. X        put(state_table[INI],o_a_s(Op,Arg,inter))
  947. X        MakeFSTN(l[i+1:0],inter,FIN)
  948. X        return
  949. X    }
  950. X    }
  951. X
  952. X    # WE SHOULD NOW BE DONE INSERTING EVERYTHING INTO state_table
  953. X    # IF WE GET TO HERE, WE'VE PARSED INCORRECTLY!
  954. X    err_out(l,4)
  955. X
  956. Xend
  957. X
  958. X
  959. X
  960. Xprocedure NextState(new)
  961. X    static nextstate
  962. X    if \new then nextstate := 1
  963. X    else nextstate +:= 1
  964. X    return nextstate
  965. Xend
  966. X
  967. X
  968. X
  969. Xprocedure err_out(x,i,elem)
  970. X    writes(&errout,"Error number ",i," parsing ",image(x)," at ")
  971. X    if \elem 
  972. X    then write(&errout,image(elem),".")
  973. X    else write(&errout,"(?).")
  974. X    exit(i)
  975. Xend
  976. X
  977. X
  978. X
  979. Xprocedure zSucceed()
  980. X    return .&pos
  981. Xend
  982. X
  983. X
  984. X
  985. Xprocedure Expand(s)
  986. X
  987. X    s2 := ""
  988. X    s ? {
  989. X    s2 ||:= ="^"
  990. X    s2 ||:= ="-"
  991. X    while s2 ||:= tab(find("-")-1) do {
  992. X        if (c1 := move(1), ="-",
  993. X        c2 := move(1),
  994. X        c1 << c2)
  995. X        then every s2 ||:= char(ord(c1) to ord(c2))
  996. X        else s2 ||:= 1(move(2), not(pos(0))) | err_out(s,2,"-")
  997. X    }
  998. X    s2 ||:= tab(0)
  999. X    }
  1000. X    return s2
  1001. X
  1002. Xend
  1003. X
  1004. X
  1005. X
  1006. Xprocedure tab_bal(l,i1,i2)
  1007. X    i := 0
  1008. X    i1_count := 0; i2_count := 0
  1009. X    while (i +:= 1) <= *l do {
  1010. X    case l[i] of {
  1011. X        i1  : i1_count +:= 1
  1012. X        i2  : i2_count +:= 1
  1013. X    }
  1014. X    if i1_count = i2_count
  1015. X    then suspend i
  1016. X    }
  1017. Xend
  1018. X
  1019. X
  1020. Xprocedure match_positive_ints(l)
  1021. X    
  1022. X    # Matches the longest sequence of positive integers in l,
  1023. X    # beginning at l[1], which neither contains, nor is fol-
  1024. X    # lowed by a negative integer.  Returns the first position
  1025. X    # after the match.  Hence, given [55, 55, 55, -42, 55],
  1026. X    # match_positive_ints will return 3.  [55, -42] will cause
  1027. X    # it to fail rather than return 1 (NOTE WELL!).
  1028. X
  1029. X    every i := 1 to *l do {
  1030. X    if l[i] < 0
  1031. X    then return (3 < i) - 1 | fail
  1032. X    }
  1033. X    return *l + 1
  1034. X
  1035. Xend
  1036. X
  1037. X
  1038. Xprocedure Ints2String(l)
  1039. X    tmp := ""
  1040. X    every tmp ||:= char(!l)
  1041. X    return tmp
  1042. Xend
  1043. X
  1044. X
  1045. Xprocedure StripChar(s,s2)
  1046. X    if find(s2,s) then {
  1047. X    tmp := ""
  1048. X    s ? {
  1049. X        while tmp ||:= tab(find("s2"))
  1050. X        do tab(many(cset(s2)))
  1051. X        tmp ||:= tab(0)
  1052. X    }
  1053. X    }
  1054. X    return \tmp | s
  1055. Xend
  1056. END_OF_FILE
  1057.   if test 20702 -ne `wc -c <'findre.icn'`; then
  1058.     echo shar: \"'findre.icn'\" unpacked with wrong size!
  1059.   fi
  1060.   # end of 'findre.icn'
  1061. fi
  1062. if test -f 'getkeys.icn' -a "${1}" != "-c" ; then 
  1063.   echo shar: Will not clobber existing file \"'getkeys.icn'\"
  1064. else
  1065.   echo shar: Extracting \"'getkeys.icn'\" \(1862 characters\)
  1066.   sed "s/^X//" >'getkeys.icn' <<'END_OF_FILE'
  1067. X############################################################################
  1068. X#
  1069. X#    Name:     getkeys.icn
  1070. X#
  1071. X#    Title:     get keys for a gettext file
  1072. X#
  1073. X#    Author:     Richard L. Goerwitz
  1074. X#
  1075. X#    Version: 1.1
  1076. X#
  1077. X############################################################################
  1078. X#
  1079. X#  Getkeys(FNAME) generates all keys in FNAME in order of occurrence.
  1080. X#  See gettext.icn for a description of the requisite file structure
  1081. X#  for FNAME.
  1082. X#
  1083. X############################################################################
  1084. X#
  1085. X#  Links: ./adjuncts.icn
  1086. X#  Requires: UNIX (maybe MS-DOS; untested)
  1087. X#  See also gettext.icn
  1088. X#
  1089. X############################################################################
  1090. X
  1091. X
  1092. X# declared in adjuncts.icn
  1093. X# global _slash, _baselen
  1094. X
  1095. Xprocedure getkeys(FNAME)
  1096. X
  1097. X    local line, intext, start_unindexed_part
  1098. X    initial {
  1099. X    if /_slash then {
  1100. X        if find("UNIX", &features) then {
  1101. X        _slash := "/"
  1102. X        _baselen := 10
  1103. X        }
  1104. X        else if find("MS-DOS", &features) then {
  1105. X        _slash := "\\"
  1106. X        _baselen := 8
  1107. X        }
  1108. X        else stop("getkeys:  OS not supported")
  1109. X    }
  1110. X    }
  1111. X
  1112. X    /FNAME & stop("error (getkeys):  null argument")
  1113. X
  1114. X    # Try to open index file (there may not be one).
  1115. X    if intext := open(Pathname(FNAME) || getidxname(FNAME)) then {
  1116. X    # If there's an index file, then just suspend all the keys in
  1117. X    # it (i.e. suspend every line except the first, upto the tab).
  1118. X    # The first line tells how many bytes in FNAME were indexed.
  1119. X    # save it, and use it to seek to unindexed portions later on.
  1120. X    start_unindexed_part := integer(read(intext))
  1121. X    while line := read(intext) do
  1122. X        line ? suspend tab(find("\t")) \ 1
  1123. X    close(intext)
  1124. X    }
  1125. X
  1126. X    intext := open(FNAME) | stop("getkeys:  ",FNAME," not found")
  1127. X    seek(intext, \start_unindexed_part | 1)
  1128. X    while line := read(intext) do
  1129. X    line ? { suspend (="::", tab(0)) \ 1 }
  1130. X
  1131. X    # Nothing left to suspend, so fail.
  1132. X    fail
  1133. X
  1134. Xend
  1135. X
  1136. END_OF_FILE
  1137.   if test 1862 -ne `wc -c <'getkeys.icn'`; then
  1138.     echo shar: \"'getkeys.icn'\" unpacked with wrong size!
  1139.   fi
  1140.   # end of 'getkeys.icn'
  1141. fi
  1142. if test -f 'gettext.icn' -a "${1}" != "-c" ; then 
  1143.   echo shar: Will not clobber existing file \"'gettext.icn'\"
  1144. else
  1145.   echo shar: Extracting \"'gettext.icn'\" \(6260 characters\)
  1146.   sed "s/^X//" >'gettext.icn' <<'END_OF_FILE'
  1147. X############################################################################
  1148. X#
  1149. X#    Name:     gettext.icn
  1150. X#
  1151. X#    Title:     gettext (simple text-base routines)
  1152. X#
  1153. X#    Author:     Richard L. Goerwitz
  1154. X#
  1155. X#    Version: 1.16
  1156. X#
  1157. X############################################################################
  1158. X#
  1159. X#  Gettext() and associated routines allow the user to maintain a file
  1160. X#  of KEY/value combinations such that a call to gettext(KEY, FNAME)
  1161. X#  will produce value.  Gettext() fails if no such KEY exists.
  1162. X#  Returns an empty string if the key exists, but has no associated
  1163. X#  value in the file, FNAME.
  1164. X#
  1165. X#  The file format is simple.  Keys belong on separate lines, marked
  1166. X#  as such by an initial colon+colon (::).  Values begin on the line
  1167. X#  following their respective keys, and extend up to the next
  1168. X#  colon+colon-initial line or EOF.  E.g.
  1169. X#
  1170. X#    ::sample.1
  1171. X#    Notice how the key above, sample.1, has :: prepended to mark it
  1172. X#    out as a key.  The text you are now reading represents that key's
  1173. X#    value.  To retrieve this text, you would call gettext() with the
  1174. X#    name of the key passed as its first argument, and the name of the
  1175. X#    file in which this text is stored as its second argument (as in
  1176. X#    gettext("sample.1","tmp.idx")).
  1177. X#    ::next.key
  1178. X#    etc...
  1179. X#
  1180. X#  For faster access, an indexing utility is included, idxtext.  Idxtext
  1181. X#  creates a separate index for a given text-base file.  If an index file
  1182. X#  exists in the same directory as FNAME, gettext() will make use of it.
  1183. X#  The index becomes worthwhile (at least on my system) after the text-
  1184. X#  base file becomes longer than 5 kilobytes.
  1185. X#
  1186. X#  Donts:
  1187. X#      1) Don't nest gettext text-base files.
  1188. X#      2) Don't use spaces and/or tabs in key names.
  1189. X#      3) Don't modify indexed files in any way other than to append
  1190. X#         additional keys/values (unless you want to re-index).
  1191. X#
  1192. X#  This program is intended for situations where keys tend to have
  1193. X#  very large values, and use of an Icon table structure would be
  1194. X#  unweildy.
  1195. X#
  1196. X#  BUGS:  Gettext() relies on the Icon runtime system and the OS to
  1197. X#  make sure the last text/index file it opens gets closed.
  1198. X#
  1199. X#  Note:  This program is NOT YET TESTED UNDER DOS.  In particular,
  1200. X#  I have no idea whether the indexing mechanism will work, due to
  1201. X#  translation that has to be done on MS-DOS text files.
  1202. X#
  1203. X############################################################################
  1204. X#
  1205. X#  Links: ./adjuncts.icn
  1206. X#
  1207. X#  Requires: UNIX (maybe MS-DOS; untested)
  1208. X#
  1209. X############################################################################
  1210. X
  1211. X# declared in adjuncts.icn
  1212. X# global _slash, _baselen
  1213. X
  1214. Xprocedure gettext(KEY,FNAME)
  1215. X
  1216. X    local line, value
  1217. X    static last_FNAME, intext, inidx
  1218. X    initial {
  1219. X    if find("UNIX", &features) then {
  1220. X        _slash := "/"
  1221. X        _baselen := 10
  1222. X    }
  1223. X    else if find("MS-DOS", &features) then {
  1224. X        _slash := "\\"
  1225. X        _baselen := 8
  1226. X    }
  1227. X    else stop("gettext:  OS not supported")
  1228. X    }
  1229. X
  1230. X    (/KEY | /FNAME) & stop("error (gettext):  null argument")
  1231. X
  1232. X    if FNAME == \last_FNAME then {
  1233. X    seek(intext, 1)
  1234. X    seek(\inidx, 1)
  1235. X    }
  1236. X    else {
  1237. X    # We've got a new text-base file.  Close the old one.
  1238. X    every close(\intext | \inidx)
  1239. X        # Try to open named text-base file.
  1240. X    intext := open(FNAME) | stop("gettext:  ",FNAME," not found")
  1241. X        # Try to open index file.
  1242. X    inidx := open(Pathname(FNAME) || getidxname(FNAME)) | &null
  1243. X    }
  1244. X    last_FNAME := FNAME
  1245. X
  1246. X    # Find offsets for key KEY in index file.  If inidx (the index
  1247. X    # file) is null (which happens when none was found), get_offsets()
  1248. X    # defaults to 1.  Otherwise it returns the offset for KEY in the
  1249. X    # index file, and then returns the last indexed byte of the file.
  1250. X    # Returning the last indexed byte lets us seek to the end and do a
  1251. X    # sequential search of any key/value entries that have been added
  1252. X    # since the last time idxtext was run.
  1253. X
  1254. X    seek(intext, get_offsets(KEY, inidx))
  1255. X
  1256. X    # Find key.  Should be right there, unless the user has appended
  1257. X    # key/value pairs to the end without re-indexing, or else has not
  1258. X    # bothered to index in the first place.  In this case we're
  1259. X    # supposed to start a sequential search for KEY upto EOF.
  1260. X
  1261. X    while line := (read(intext) | fail) do {
  1262. X    line ? {
  1263. X        if (="::", =KEY, pos(0))
  1264. X        then break
  1265. X    }
  1266. X    }
  1267. X
  1268. X    # Collect all text upto the next colon+colon-initial line (::)
  1269. X    # or EOF.
  1270. X    value := ""
  1271. X    while line := read(intext) do {
  1272. X    match("::",line) & break
  1273. X    value ||:= line || "\n"
  1274. X    }
  1275. X
  1276. X    # Note that a key with an empty value returns an empty string.
  1277. X    return trim(value, '\n')
  1278. X
  1279. Xend
  1280. X
  1281. X
  1282. X
  1283. Xprocedure get_offsets(KEY, inidx)
  1284. X
  1285. X    local bottom, top, loc, firstpart, offset
  1286. X    # Use these to store values likely to be reused.
  1287. X    static old_inidx, firstline, SOF, EOF
  1288. X
  1289. X    # If there's no index file, then just return an offset of 1.
  1290. X    if /inidx then
  1291. X    return 1
  1292. X
  1293. X    # First line contains offset of last indexed byte in the main
  1294. X    # text file.  We need this later.  Save it.  Start the binary
  1295. X    # search routine at the next byte after this line.
  1296. X    seek(inidx, 1)
  1297. X    if not (inidx === \old_inidx) then {
  1298. X
  1299. X    # Get first line.
  1300. X    firstline := !inidx
  1301. X    # Set "bottom."
  1302. X    1 = (SOF := where(inidx)-1) &
  1303. X        stop("get_offsets:  corrupt .IDX file; reindex")
  1304. X    # How big is this file?
  1305. X    seek(inidx, 0)
  1306. X    EOF := where(inidx)
  1307. X
  1308. X    old_inidx := inidx
  1309. X    }
  1310. X    # SOF, EOF constant for a given inidx file.
  1311. X    bottom := SOF; top := EOF
  1312. X
  1313. X    # If bottom gets bigger than top, there's no such key.
  1314. X    until bottom > top do {
  1315. X
  1316. X    loc := (top+bottom) / 2
  1317. X    seek(inidx, loc)
  1318. X
  1319. X    # Move past next newline.  If at EOF, break.
  1320. X    incr := 1
  1321. X    until reads(inidx) == "\n" do
  1322. X        incr +:= 1
  1323. X    if loc+incr = EOF then {
  1324. X        top := loc-1
  1325. X        next
  1326. X    }
  1327. X
  1328. X    # Check to see if the current line contains KEY.
  1329. X    read(inidx) ? {
  1330. X
  1331. X        # .IDX file line format is KEY\toffset
  1332. X        firstpart := tab(find("\t"))
  1333. X        if KEY == firstpart then {
  1334. X        # return offset
  1335. X        return (move(1), tab(0))
  1336. X        }
  1337. X        # Ah, this is what all binary searches do.
  1338. X        else {
  1339. X        if KEY << firstpart
  1340. X        then top := loc-1
  1341. X        else bottom := loc + incr + *&subject
  1342. X        }
  1343. X    }
  1344. X    }
  1345. X
  1346. X    # First line of the index file contains offset of last indexed
  1347. X    # byte + 1.  Might be the only line in the file (if it had no
  1348. X    # keys when it was indexed).
  1349. X    return firstline
  1350. X
  1351. Xend
  1352. END_OF_FILE
  1353.   if test 6260 -ne `wc -c <'gettext.icn'`; then
  1354.     echo shar: \"'gettext.icn'\" unpacked with wrong size!
  1355.   fi
  1356.   # end of 'gettext.icn'
  1357. fi
  1358. if test -f 'idxtext.icn' -a "${1}" != "-c" ; then 
  1359.   echo shar: Will not clobber existing file \"'idxtext.icn'\"
  1360. else
  1361.   echo shar: Extracting \"'idxtext.icn'\" \(3452 characters\)
  1362.   sed "s/^X//" >'idxtext.icn' <<'END_OF_FILE'
  1363. X############################################################################
  1364. X#
  1365. X#    Name:     idxtext.icn
  1366. X#
  1367. X#    Title:     idxtext (index text-base for gettext() routine)
  1368. X#
  1369. X#    Author:     Richard L. Goerwitz
  1370. X#
  1371. X#    Version: 1.11
  1372. X#
  1373. X############################################################################
  1374. X#
  1375. X#      Idxtext turns a file associated with gettext() routine into an
  1376. X#  indexed text-base.  Though gettext() will work fine with files
  1377. X#  that haven't been indexed via idxtext(), access is faster if the
  1378. X#  indexing is done if the file is, say, over 10k (on my system the
  1379. X#  crossover point is actually about 5k).
  1380. X#
  1381. X#      Usage is simply "idxtext [-a] file1 [file2 [...]]," where file1,
  1382. X#  file2, etc are the names of gettext-format files that are to be
  1383. X#  (re-)indexed.  The -a flag tells idxtext to abort if an index file
  1384. X#  already exists.
  1385. X#
  1386. X#      Indexed files have a very simple format: keyname tab offset
  1387. X#  [tab offset [etc.]]\n.  The first line of the index file is a
  1388. X#  pointer to the last indexed byte of the text-base file it indexes.
  1389. X#
  1390. X#  BUGS: Index files are too large.  Also, I've yet to find a portable
  1391. X#  way of creating unique index names that are capable of being
  1392. X#  uniquely identified with their original text file.  It might be
  1393. X#  sensible to hard code the name into the index.  The chances of a
  1394. X#  conflict seem remote enough that I haven't bothered.  If you're
  1395. X#  worried, use the -a flag.
  1396. X#
  1397. X############################################################################
  1398. X#
  1399. X#  Links: ./adjuncts.icn
  1400. X#  Requires: UNIX or MS-DOS
  1401. X#  See also: gettext.icn
  1402. X#
  1403. X############################################################################
  1404. X
  1405. X
  1406. X# declared in adjuncts.icn
  1407. X# global _slash, _baselen
  1408. X
  1409. Xprocedure main(a)
  1410. X
  1411. X    local ABORT, idxfile_name, fname, infile, outfile
  1412. X    initial {
  1413. X    if find("UNIX", &features) then {
  1414. X        _slash := "/"
  1415. X        _baselen := 10
  1416. X    }
  1417. X    else if find("MS-DOS", &features) then {
  1418. X        _slash := "\\"
  1419. X        _baselen := 8
  1420. X    }
  1421. X    else stop("idxtext:  OS not supported")
  1422. X    }
  1423. X
  1424. X    if \a[1] == "-a" then ABORT := pop(a)    
  1425. X
  1426. X    # Check to see if we have any arguments.
  1427. X    *a = 0 & stop("usage: idxtext [-a] file1 [file2 [...]]")
  1428. X
  1429. X    # Start popping filenames off of the argument list.
  1430. X    while fname := pop(a) do {
  1431. X
  1432. X    # Open input file.
  1433. X    infile := open(fname) |
  1434. X        { write(&errout, "idxtext:  ",fname," not found"); next }
  1435. X    # Get index file name.
  1436. X    idxfile_name := Pathname(fname) || getidxname(fname)
  1437. X    if \ABORT then if close(open(idxfile_name)) then
  1438. X        stop("idxtext:  index file ",idxfile_name, " already exists")
  1439. X    outfile := open(idxfile_name, "w") |
  1440. X        stop("idxtext:  can't open ", idxfile_name)
  1441. X
  1442. X    # Write index to index.IDX file.
  1443. X    write_index(infile, outfile)
  1444. X
  1445. X    every close(infile | outfile)
  1446. X
  1447. X    }
  1448. X
  1449. Xend
  1450. X
  1451. X
  1452. Xprocedure write_index(in, out)
  1453. X
  1454. X    local key_offset_table, w, line, KEY
  1455. X
  1456. X    # Write to out all keys in file "in," with their byte
  1457. X    # offsets.
  1458. X
  1459. X    key_offset_table := table()
  1460. X
  1461. X    while (w := where(in), line := read(in)) do {
  1462. X    line ? {
  1463. X        if ="::" then {
  1464. X        KEY := trim(tab(0))
  1465. X        if not (/key_offset_table[KEY] := KEY || "\t" || w)
  1466. X        then stop("idxtext:  duplicate key, ",KEY)
  1467. X        }
  1468. X    }
  1469. X    }
  1470. X
  1471. X    # First line of index contains the offset of the last
  1472. X    # indexed byte in write_index, so that we can still
  1473. X    # search unindexed parts of in.
  1474. X    write(out, where(in))
  1475. X
  1476. X    # Write sorted KEY\toffset lines.
  1477. X    if *key_offset_table > 0 then
  1478. X    every write(out, (!sort(key_offset_table))[2])
  1479. X
  1480. X    return
  1481. X
  1482. Xend
  1483. END_OF_FILE
  1484.   if test 3452 -ne `wc -c <'idxtext.icn'`; then
  1485.     echo shar: \"'idxtext.icn'\" unpacked with wrong size!
  1486.   fi
  1487.   # end of 'idxtext.icn'
  1488. fi
  1489. if test -f 'jarg2get.icn' -a "${1}" != "-c" ; then 
  1490.   echo shar: Will not clobber existing file \"'jarg2get.icn'\"
  1491. else
  1492.   echo shar: Extracting \"'jarg2get.icn'\" \(1653 characters\)
  1493.   sed "s/^X//" >'jarg2get.icn' <<'END_OF_FILE'
  1494. X############################################################################
  1495. X#
  1496. X#    Name:     1.1
  1497. X#
  1498. X#    Title:     jargon to gettext format converter
  1499. X#
  1500. X#    Author:     Richard L. Goerwitz
  1501. X#
  1502. X#    Version: jarg2get.icn
  1503. X#
  1504. X############################################################################
  1505. X#  
  1506. X#  Converts jargon.ascii (stdin) to a format suitable for use by gettext.
  1507. X#  Writes to stdout.  Jargon.ascii was posted recently (c. March 1, 1991)
  1508. X#  to alt.sources.
  1509. X#
  1510. X############################################################################
  1511. X
  1512. Xprocedure main()
  1513. X
  1514. X    local line, KEY, key_set, no, yes, blank_count
  1515. X
  1516. X    blank_count := 0
  1517. X    key_set := set()
  1518. X    no := &ucase || "-/"; yes := &lcase || "  "
  1519. X    # Isn't goal-directed evaluation nice?
  1520. X    (match("= A =", !&input), "" == !&input)
  1521. X
  1522. X    # Read stdin, looking for entries.  Entries can be distinguished
  1523. X    # a) by a preceding blank line, and b) by the presence of charac-
  1524. X    # ters beginning immediately at the margin, and c) by the presence
  1525. X    # of a colon plus a space on the line.
  1526. X    while line := trim(read(), '\t \xFF\r') do {
  1527. X
  1528. X    if "" == line then {
  1529. X        if (blank_count +:= 1) > 2
  1530. X        then exit(0)
  1531. X        else write()
  1532. X    }
  1533. X    else {
  1534. X        line ? {
  1535. X            if match("Hacker Folklore"|"Appendix A: ")
  1536. X        then exit(0)
  1537. X        if blank_count > 0 &
  1538. X           KEY := map(tab(any(&letters)) || tab(find(": ")),no,yes)
  1539. X        then {
  1540. X            KEY := trim(KEY,' :')
  1541. X            if not member(key_set, KEY)
  1542. X            then write("::", KEY)
  1543. X            insert(key_set, KEY)
  1544. X        }
  1545. X        (="= ", tab(any(&ucase)), =" =", !&input) | write(line)
  1546. X        }
  1547. X        blank_count := 0
  1548. X    }
  1549. X    }
  1550. X
  1551. X    stop("jarg2get:  aborting (are you sure you have the correct file?)")
  1552. X
  1553. Xend
  1554. END_OF_FILE
  1555.   if test 1653 -ne `wc -c <'jarg2get.icn'`; then
  1556.     echo shar: \"'jarg2get.icn'\" unpacked with wrong size!
  1557.   fi
  1558.   # end of 'jarg2get.icn'
  1559. fi
  1560. if test -f 'jargon.src' -a "${1}" != "-c" ; then 
  1561.   echo shar: Will not clobber existing file \"'jargon.src'\"
  1562. else
  1563.   echo shar: Extracting \"'jargon.src'\" \(3147 characters\)
  1564.   sed "s/^X//" >'jargon.src' <<'END_OF_FILE'
  1565. X############################################################################
  1566. X#
  1567. X#    Name:     1.10
  1568. X#
  1569. X#    Title:     look up words in hackers' jargon database
  1570. X#
  1571. X#    Author:     Richard L. Goerwitz
  1572. X#
  1573. X#    Version: jargon.icn
  1574. X#
  1575. X############################################################################
  1576. X#
  1577. X#  Defines hackers' jargon.  Usage is simply "jargon word," where word
  1578. X#  is some bit of hacker's slang for which a definition is desired.
  1579. X#  Aborts with an exit code of 1 on no-arg invocation.  If a "word"
  1580. X#  arg is given, but no definition is found, jargon exits with status
  1581. X#  2.  Otherwise the appropriate entry for "word" is displayed.  If
  1582. X#  you aren't sure of precisely what word you are looking for, then
  1583. X#  you can type "jargon -p pattern" (where pattern is an egrep-style
  1584. X#  regular expression).  Jargon will provide a list of entries con-
  1585. X#  taining pattern on the standard output.  Note that this option will
  1586. X#  probably be very slow for non-UNIX installations.
  1587. X#
  1588. X#  Tested on the jargon file, version 2.7.1 (posted to alt.sources on
  1589. X#  March 1, 1991).  Tested also on the 2.8.3 version FTPed from one or
  1590. X#  another archive site that I don't recall offhand.  It may work on
  1591. X#  other versions as well.
  1592. X#
  1593. X############################################################################
  1594. X#
  1595. X#  Links:  gettext.icn, adjuncts.icn (getkeys.icn, findre.icn)
  1596. X#
  1597. X############################################################################
  1598. X
  1599. Xprocedure main(a)
  1600. X
  1601. X    local n, usage, no, yes, is_UNIX, firstarg, pat, cmd, entry
  1602. X
  1603. X    # Change this, if you use a different location.
  1604. X    n := "/usr/local/lib/jargon/jargon.wrd"
  1605. X    is_UNIX := find("UNIX", &features)
  1606. X
  1607. X    no := &ucase || "-/"; yes := &lcase || "  "
  1608. X    usage := "<usage> ::=  <progname> <arguments> \n_
  1609. X          <progname> ::= \"jargon\" \n_
  1610. X           <arguments> ::= <word> | \"-p\" <pattern>"
  1611. X    firstarg := pop(a) | stop(usage)
  1612. X
  1613. X    if firstarg == "-p" then {
  1614. X
  1615. X    # User wants to see a list of entries which match a pattern.
  1616. X    pat := map(pop(a), no, yes) | stop(usage)
  1617. X    # If there are still more arguments, the user has screwed up.
  1618. X    *a = 0 | stop(usage)
  1619. X
  1620. X    # Search for pat in the list of entries.  If running under UNIX,
  1621. X    # egrep the index file.  Otherwise, use an Icon-only egrep sub-
  1622. X    # stitute (slow).
  1623. X    if \is_UNIX then {
  1624. X        # Ah, UNIX.  Use the system egrep command to match pat.
  1625. X        _slash := "/"; _baselen := 10
  1626. X        cmd := "egrep '"|| pat ||".*\t' "|| Pathname(n) || getidxname(n)
  1627. X        in := open(cmd, "pr") | stop("error (main): can't egrep IDX file")
  1628. X        every entry := !in do
  1629. X        entry ? write(1(tab(find("\t")+1), tab(many(&digits)), pos(0)))
  1630. X        close(in)
  1631. X    }
  1632. X    else {
  1633. X        # Not UNIX.  Use (slow) Icon-only regexp handler.
  1634. X        every entry := getkeys(n) do {
  1635. X        if findre(pat, entry) then
  1636. X            write(entry)
  1637. X        }
  1638. X    }
  1639. X    # If any entries contained pat, then exit with zero status.
  1640. X    if \entry then exit(0)
  1641. X    else exit(2)
  1642. X    }
  1643. X
  1644. X    # Firstarg is not -p, and the sole argument must name an entry the
  1645. X    # user wants retrieved.
  1646. X    else {
  1647. X    # If we still have elements in a, the user has screwed up.
  1648. X    *a = 0 | stop(usage)
  1649. X    write(gettext(trim(map(firstarg, no, yes)), n)) | exit(2)
  1650. X    exit(0)
  1651. X    }
  1652. X
  1653. Xend 
  1654. END_OF_FILE
  1655.   if test 3147 -ne `wc -c <'jargon.src'`; then
  1656.     echo shar: \"'jargon.src'\" unpacked with wrong size!
  1657.   fi
  1658.   # end of 'jargon.src'
  1659. fi
  1660. echo shar: End of archive 1 \(of 1\).
  1661. cp /dev/null ark1isdone
  1662. MISSING=""
  1663. for I in 1 ; do
  1664.     if test ! -f ark${I}isdone ; then
  1665.     MISSING="${MISSING} ${I}"
  1666.     fi
  1667. done
  1668. if test "${MISSING}" = "" ; then
  1669.     echo You have the archive.
  1670.     rm -f ark[1-9]isdone
  1671. else
  1672.     echo You still must unpack the following archives:
  1673.     echo "        " ${MISSING}
  1674. fi
  1675. exit 0
  1676. exit 0 # Just in case...
  1677. -- 
  1678. Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
  1679. Sterling Software, IMD           UUCP:     uunet!sparky!kent
  1680. Phone:    (402) 291-8300         FAX:      (402) 291-4362
  1681. Please send comp.sources.misc-related mail to kent@uunet.uu.net.
  1682.